Skip to content

[FEATURE] Add OpenSearch datasource and log query plugin with PPL support#641

Open
Oliver-ke wants to merge 13 commits into
perses:mainfrom
Oliver-ke:feature/opensearch-plugin
Open

[FEATURE] Add OpenSearch datasource and log query plugin with PPL support#641
Oliver-ke wants to merge 13 commits into
perses:mainfrom
Oliver-ke:feature/opensearch-plugin

Conversation

@Oliver-ke
Copy link
Copy Markdown

@Oliver-ke Oliver-ke commented Apr 27, 2026

Description

Adds an OpenSearch datasource + log query plugin

These changes includes:

  • Plugin scaffold — OpenSearchDatasource + OpenSearchLogQuery plugins (TypeScript, React, Module Federation via rsbuild).
  • PPL client (opensearch-client.ts) - POSTs to /_plugins/_ppl, handles relative datasource URLs, throws a typed OpenSearchPPLError(status, body) on non-200.
  • Time bounding + row mapping (get-opensearch-log-data.ts) - wraps user PPL with a where clause on the configured timestamp field; maps rows to LogEntry and surfaces
    remaining columns (e.g. traceId) as labels.
  • Field-name overrides - timestampField / messageField on the query spec for indexes that don't use @timestamp / message (e.g. OpenTelemetry's body).
  • Editor (OpenSearchLogQueryEditor.tsx) - datasource picker, index pattern, timestamp/message field overrides, PPL textarea, and an inline Alert driven by an optional
    queryError prop (parses error.reason / error.details from the OpenSearch error JSON).
  • Trace-to-logs pivot - relies on the existing replaceVariables path, so a $traceId dashboard variable substitutes into the PPL before send. Example dashboard at
    opensearch/docs/examples/trace-to-logs.json (Tempo + OpenSearch; Jaeger swap documented).
  • CUE schema (schemas/queries/opensearch-log-query/query.cue) - mirrors the TS spec including the new optional fields.
  • Go SDK (sdk/go/query/log) - PluginSpec + TimestampField() / MessageField() options for dashboards-as-code.
  • Tests - 26 TS tests (PPL bounder, row mapper with overrides + fallbacks, $traceId substitution + dependsOn, PPL client URL/headers/error, editor inputs + error alert)
    and 2 Go tests (builder JSON shape + omitempty for optional fields).

Screenshots

image

DataSource
image

Checklist

  • Pull request has a descriptive title and context useful to a reviewer.
  • Pull request title follows the [<catalog_entry>] <commit message> naming convention using one of the
    following catalog_entry values: FEATURE, ENHANCEMENT, BUGFIX, BREAKINGCHANGE, DOC,IGNORE.
  • All commits have DCO signoffs.

UI Changes

  • Changes that impact the UI include screenshots and/or screencasts of the relevant changes.
  • Code follows the UI guidelines.

Signed-off-by: Azorji Kelechi Oliver <kelechioliver96@gmail.com>
Oliver-ke added 6 commits May 10, 2026 05:34
Signed-off-by: Azorji Kelechi Oliver <kelechioliver96@gmail.com>
The bound was being appended at the end of the user query, which fails
once the user pipeline drops @timestamp from the schema (stats, fields,
top). Tests now describe the intended ordering. Implementation follows.

Signed-off-by: Azorji Kelechi Oliver <kelechioliver96@gmail.com>
Signed-off-by: Azorji Kelechi Oliver <kelechioliver96@gmail.com>
Signed-off-by: Azorji Kelechi Oliver <kelechioliver96@gmail.com>
Signed-off-by: Azorji Kelechi Oliver <kelechioliver96@gmail.com>
…cker file

Signed-off-by: Azorji Kelechi Oliver <kelechioliver96@gmail.com>
@Oliver-ke Oliver-ke force-pushed the feature/opensearch-plugin branch from 0963d7f to 496c296 Compare May 10, 2026 04:35
Oliver-ke added 6 commits May 10, 2026 08:18
Signed-off-by: Azorji Kelechi Oliver <kelechioliver96@gmail.com>
Signed-off-by: Azorji Kelechi Oliver <kelechioliver96@gmail.com>
Signed-off-by: Azorji Kelechi Oliver <kelechioliver96@gmail.com>
Signed-off-by: Azorji Kelechi Oliver <kelechioliver96@gmail.com>
Signed-off-by: Azorji Kelechi Oliver <kelechioliver96@gmail.com>
…search-plugin

chore(plugins): update with remote main branch
@Oliver-ke Oliver-ke marked this pull request as ready for review May 10, 2026 17:13
@Oliver-ke Oliver-ke requested review from a team, AntoineThebaud and Nexucis as code owners May 10, 2026 17:13
@Oliver-ke Oliver-ke requested review from jgbernalp and removed request for a team May 10, 2026 17:13
@ibakshay
Copy link
Copy Markdown
Contributor

Hi @Oliver-ke,
Great work! 👏
Could you please write down on how one can test this plugin locally?

@Oliver-ke
Copy link
Copy Markdown
Author

Hi @Oliver-ke, Great work! 👏 Could you please write down on how one can test this plugin locally?

Sure, I'd share my testing.md file shortly

@Oliver-ke
Copy link
Copy Markdown
Author

@ibakshay

TESTING.md

Testing the OpenSearch plugin

Manual QA steps. Verifies datasource, log query, and trace → logs pivot.

Prerequisites

  • Node 18.17+ (Jest needs os.availableParallelism)
  • npm
  • Go 1.21+ (for the SDK tests)
  • Docker (for local OpenSearch)
  • Perses dev server running locally (see top-level README.md)
  • percli on $PATH (built from the perses repo: make build produces bin/percli)

1. Run unit tests

Install once at the workspace root so react/react-dom hoist correctly. Running
npm install inside the plugin directory creates a duplicate React install and
breaks the component tests with Cannot read properties of null (reading 'useState').

cd plugins
npm install
cd opensearch
npm test
npm run type-check
npm run lint

Validate the plugin schemas with percli (built from the perses repo):

cd plugins/opensearch
percli plugin lint
percli plugin test-schemas

Go SDK:

cd plugins/opensearch
go test ./sdk/...

Expect all green. No skipped suites.

2. Start a local OpenSearch

Use the bundled compose file (also boots OpenSearch Dashboards on :5601).
The compose file enables CORS so the Perses UI (running on :3000) can hit
the cluster directly when the datasource uses directUrl. Without these
headers the browser preflight is blocked and queries fail silently with no
status code in the network tab.

cd docs/examples
docker compose up -d
docker compose logs -f opensearch    # watch boot, Ctrl+C once "started" appears

Wait until ready:

curl -s http://localhost:9200/_cluster/health | jq .status

Expect "green" or "yellow".

Teardown when done:

docker compose down -v

3. Seed sample log data

Index two log documents with a shared traceId:

curl -X POST "http://localhost:9200/otel-logs-2026.04.29/_doc" \
  -H 'Content-Type: application/json' -d '{
    "@timestamp": "2026-04-29T10:00:00Z",
    "body": "request received",
    "traceId": "abc123",
    "severity": "INFO"
  }'

curl -X POST "http://localhost:9200/otel-logs-2026.04.29/_doc" \
  -H 'Content-Type: application/json' -d '{
    "@timestamp": "2026-04-29T10:00:01Z",
    "body": "request failed",
    "traceId": "abc123",
    "severity": "ERROR"
  }'

curl -X POST "http://localhost:9200/otel-logs-2026.04.29/_refresh"

Sanity-check PPL endpoint directly:

curl -X POST "http://localhost:9200/_plugins/_ppl" \
  -H 'Content-Type: application/json' \
  -d '{"query":"source=otel-logs-* | where traceId='\''abc123'\''"}'

Expect 2 datarows in response.

4. Build and serve the plugin

cd plugins/opensearch
npm run dev

Dev server listens on http://localhost:3135 (configured in rsbuild.config.ts).
The module-federation manifest is served at http://localhost:3135/mf-manifest.json;
point Perses at that URL when registering the plugin. Keep the dev server running.

5. Register the datasource in Perses

The current Perses UI does not accept raw YAML for datasource creation, so create
the resource one of two ways.

Option A — via the UI form

Navigate to Admin → Global Datasources → Add Datasource. Fill in:

  • Name: opensearch
  • Plugin Options → Source: OpenSearch Datasource
  • Direct URL: http://localhost:9200

Save.

Option B — via percli (declarative)

Drop this YAML in a file opensearch-ds.yaml:

kind: GlobalDatasource
metadata:
  name: opensearch
spec:
  default: false
  plugin:
    kind: OpenSearchDatasource
    spec:
      directUrl: http://localhost:9200

Apply it:

percli login http://localhost:8080
percli apply -f opensearch-ds.yaml

Verify

  • Datasource list shows opensearch with kind OpenSearchDatasource.
  • Edit form renders without console errors. (Open browser devtools → Console
    while clicking through the form. Any red errors here block step 6.)

6. Smoke test the log query editor

Create dashboard, add LogsTable panel, pick OpenSearchLogQuery.

  • Datasource picker lists opensearch. Select it.
  • Type query: source=otel-logs-* | where traceId='abc123'.
  • Hit Apply. Table shows 2 rows. Timestamp + message columns populated.

Edge cases:

Case Input Expected
Empty query `` No request, no error toast
Bad PPL source=foo | invalid Error banner with PPL error body (SyntaxCheckException)
Wrong index source=does-not-exist Error banner with IndexNotFoundException from OpenSearch (the PPL endpoint returns 404; the plugin surfaces that as an OpenSearchPPLError)
Unreachable DS stop docker, re-run query Error banner, retry works after restart
Aggregation (stats) source=otel-logs-* | stats count() by service Returns one row per service, no error
Field projection source=otel-logs-* | fields service, body | head 10 Returns 10 rows with only those two columns

7. Field overrides

Index a doc with non-standard field names:

curl -X POST "http://localhost:9200/legacy-logs/_doc" \
  -H 'Content-Type: application/json' -d '{
    "time": "2026-04-29T10:05:00Z",
    "message": "legacy log line",
    "trace_id": "abc123"
  }'
curl -X POST "http://localhost:9200/legacy-logs/_refresh"

In the query spec, set:

timestampField: time
messageField: message
query: source=legacy-logs | where trace_id='abc123'

Expect row to render with time as timestamp and message as body. Without
the overrides, the row still renders without crashing: missing timestamp falls
back to epoch 0, and missing message falls back to a JSON dump of the row.

8. Trace → logs pivot

Load docs/examples/trace-to-logs.json as a dashboard. Requires a Tempo (or Jaeger) datasource also configured.

  • Set traceId variable to abc123.
  • Logs panel re-runs and shows the 2 seeded rows.
  • Change variable to xyz999. Logs panel goes empty without error.
  • Swap Tempo panel for Jaeger per docs/examples/README.md. Logs panel unchanged.

9. Cleanup

cd plugins/opensearch/docs/examples
docker compose down -v

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants